"""
:mod:`CalcTrackDomain` - calculate track generation domain
==========================================================

.. module:: CalcTrackDomain
    :synopsis: Determine track generator domain, ensuring it encompasses
               all tracks entering the windfield generator domain.

.. moduleauthor:: Nicholas Summons <nicholas.summons@ga.gov.au>


Determine track generator domain, ensuring it encompasses
all tracks entering the windfield generator domain.

CreationDate: 2010-03-23

"""

from os.path import join as pjoin
import logging
import math

from Utilities.config import ConfigParser
from Utilities.files import flLoadFile


class CalcTrackDomain(object):
    """
    Calculate the extents over which tracks will be generated.

    Based on the user-selected wind field domain, examine the input dataset
    and identify the maximal extent covering all TCs that enter the wind
    field domain. This sets the domain over which synthetic TCs will be
    generated by teh TrackGenerator module.

    :type  configFile: string
    :param configFile: Configuration file containing simulation settings

    """

    def __init__(self, configFile):
        """
        :param str configFile: Path to configuration file.
        """
        config = ConfigParser()
        config.read(configFile)

        self.outputPath = config.get('Output', 'Path')
        self.wf_domain = config.geteval('Region', 'gridLimit')

    def calc(self, index, lons, lats):
        """
        Core function for determining track generation domain

        :param index: :class:`numpy.ndarray` of values indicating the start
                      of a new TC track (1), zero otherwise

        :param lons: :class:`numpy.ndarray` representing the longitude of all
                     TC positions
        :param lats: :class:`numpy.ndarray` representing the latitude of all
                     TC positions

        """
        logging.debug("Determining track generation domain")
        tg_domain = self.wf_domain.copy()
        track_limits = {'xMin':9999, 'xMax':-9999,
                        'yMin':9999, 'yMax':-9999}

        for [idx, lon, lat] in zip(index, lons, lats):
            if idx == 1:
                # Reset cyclone lon/lon limits
                track_limits = {'xMin':9999, 'xMax':-9999,
                                'yMin':9999, 'yMax':-9999}

            track_limits['xMin'] = min(track_limits['xMin'], lon)
            track_limits['xMax'] = max(track_limits['xMax'], lon)
            track_limits['yMin'] = min(track_limits['yMin'], lat)
            track_limits['yMax'] = max(track_limits['yMax'], lat)

            if (self.wf_domain['xMin'] <= lon <= self.wf_domain['xMax']) & \
               (self.wf_domain['yMin'] <= lat <= self.wf_domain['yMax']):

                tg_domain['xMin'] = min(tg_domain['xMin'],
                                        track_limits['xMin'])
                tg_domain['xMax'] = max(tg_domain['xMax'],
                                        track_limits['xMax'])
                tg_domain['yMin'] = min(tg_domain['yMin'],
                                        track_limits['yMin'])
                tg_domain['yMax'] = max(tg_domain['yMax'],
                                        track_limits['yMax'])

            # Extend domain to closest integer lat/lon value
            tg_domain['xMin'] = math.floor(tg_domain['xMin'])
            tg_domain['xMax'] = math.ceil(tg_domain['xMax'])
            tg_domain['yMin'] = math.floor(tg_domain['yMin'])
            tg_domain['yMax'] = math.ceil(tg_domain['yMax'])

        return tg_domain

    def calcDomainFromTracks(self, index, lons, lats):
        """
        Calculate track generation domain from input track details

        :param index: :class:`numpy.ndarray` of values indicating the start
                      of a new TC track (1), zero otherwise

        :param lons: :class:`numpy.ndarray` representing the longitude of all
                     TC positions
        :param lats: :class:`numpy.ndarray` representing the latitude of all
                     TC positions

        :rtype: dict
        :returns: the domain where the tracks will be generated.
                  The :class:`dict` should contain the keys :attr:`xMin`,
                  :attr:`xMax`, :attr:`yMin` and :attr:`yMax`. The *x*
                  variable bounds the longitude and the *y* variable
                  bounds the latitude.

        """

        tg_domain = self.calc(index, lons, lats)
        return tg_domain

    def calcDomainFromFile(self):
        """
        Calculate track generation domain, using a file as the input source

        :rtype: dict
        :returns: the domain where the tracks will be generated.
                  The :class:`dict` should contain the keys :attr:`xMin`,
                  :attr:`xMax`, :attr:`yMin` and :attr:`yMax`. The *x*
                  variable bounds the longitude and the *y* variable
                  bounds the latitude.

        """

        # Load tracks from file
        cyclone_tracks = flLoadFile(pjoin(self.outputPath, 'process',
                                          'cyclone_tracks'),
                                    '%', ',')
        tg_domain = self.calc(cyclone_tracks[:, 0],
                              cyclone_tracks[:, 1],
                              cyclone_tracks[:, 2])

        return tg_domain
